using System;
using System.Collections.Generic;
using System.Linq;
using ChibiTD.Common;
using ChibiTD.Common.Enums;
using ChibiTD.Core.Entities;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace ChibiTD.Game
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class ChibiTD : Microsoft.Xna.Framework.Game
    {
        private GraphicsDeviceManager graphics;
        private SpriteBatch batch;

        private SoldierPlayer soldier;
        private IList<BulletEntity> bullets;

        private MouseState mouseOldState;
        private KeyboardState keyboardOldState;

        public ChibiTD()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 600;
            graphics.IsFullScreen = false;
            this.IsMouseVisible = true;
            Content.RootDirectory = "Content";

            soldier = new SoldierPlayer(this, 60, 60, Vector2.Zero);
            bullets = new List<BulletEntity>();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            //Initialize Soldier
            soldier.Position = new Vector2(graphics.PreferredBackBufferWidth / 2, graphics.PreferredBackBufferHeight / 2);
            soldier.Animations = new Dictionary<AnimationState, Animation>();
            soldier.Animations.Add(AnimationState.Stopped, new Animation(240, 60, 4, 0, 0));
            soldier.Animations.Add(AnimationState.StoppedShooting, new Animation(240, 60, 4, 0, 60));
            soldier.Animations.Add(AnimationState.Walking, new Animation(240, 60, 4, 0, 120));
            soldier.Animations.Add(AnimationState.WalkingShooting, new Animation(240, 60, 4, 0, 180));
            soldier.CurrentAnimation = AnimationState.Stopped;
            soldier.LoopAnimation = true;
            soldier.Rotation = 0f;
            soldier.Speed = 1f;
            soldier.ShotAreaRadius = 100;
            soldier.ShowShotArea = false;
            soldier.ShotAreaColor = new Color(Color.Green.R, Color.Green.G, Color.Green.B, 50);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            batch = new SpriteBatch(GraphicsDevice);
            soldier.LoadTexture(@"sprites\soldier_sprite");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            MouseState mouseState = Mouse.GetState();
            KeyboardState keyboardState = Keyboard.GetState();

            Vector2 mouseDirection = new Vector2(mouseState.X, mouseState.Y) - soldier.Position;

            //Formula to calcule the soldier's angle. 
            soldier.Rotation = (float) Math.Atan2(mouseDirection.Y, mouseDirection.X);

            float posX = soldier.Position.X;
            float posY = soldier.Position.Y;

            //A new bullet is created when the mouse left button is pressed.
            if (mouseOldState.LeftButton != ButtonState.Pressed && mouseState.LeftButton == ButtonState.Pressed)
            {
                soldier.CurrentAnimation = AnimationState.WalkingShooting;
                float bulletX = soldier.Position.X + 25*(float) Math.Cos(soldier.Rotation);
                float bulletY = soldier.Position.Y + 25*(float) Math.Sin(soldier.Rotation);
                Vector2 bulletPosition = new Vector2(bulletX, bulletY);
                
                BulletEntity bullet = new BulletEntity(this, 20, 10, bulletPosition);
                bullet.LoadTexture(@"sprites\bullet");
                bullet.Speed = 10f;
                bullet.Rotation = soldier.Rotation;
                bullet.Animations = new Dictionary<AnimationState, Animation>();
                bullet.Animations.Add(AnimationState.Stopped, new Animation(20, 10, 1, 0, 0));
                bullets.Add(bullet);
            }
            else
            {
                soldier.CurrentAnimation = AnimationState.Stopped;
            }

            //Update the bullets position and speed
            for (int i = 0; i < bullets.Count; i++)
            {
                float bulletX = bullets[i].Position.X;
                float bulletY = bullets[i].Position.Y;
                bulletX += bullets[i].Speed * (float)Math.Cos(bullets[i].Rotation);
                bulletY += bullets[i].Speed * (float)Math.Sin(bullets[i].Rotation);
                bullets[i].Position = new Vector2(bulletX, bulletY);
            }
            
            //Remove the out world bullets
            for (int i = 0; i < bullets.Count; i++)
            {
                if (bullets[i].Position.X > graphics.PreferredBackBufferWidth || bullets[i].Position.X < 0)
                    bullets.RemoveAt(i);
                else if (bullets[i].Position.Y > graphics.PreferredBackBufferHeight || bullets[i].Position.Y < 0)
                    bullets.RemoveAt(i);
            }
            

            if (keyboardState.IsKeyDown(Keys.Escape))
                this.Exit();
            
            if (keyboardState.IsKeyDown(Keys.W))
            {
                soldier.CurrentAnimation = AnimationState.Walking;
                posY -= soldier.Speed;
            }
            if (keyboardState.IsKeyDown(Keys.A))
            {
                soldier.CurrentAnimation = AnimationState.Walking;
                posX -= soldier.Speed;
            }
            if (keyboardState.IsKeyDown(Keys.S))
            {
                soldier.CurrentAnimation = AnimationState.Walking;
                posY += soldier.Speed;
            }
            if (keyboardState.IsKeyDown(Keys.D))
            {
                soldier.CurrentAnimation = AnimationState.Walking;
                posX += soldier.Speed;
            }

            soldier.UpdateAnimation(gameTime);
            soldier.FireState = mouseState.LeftButton == ButtonState.Pressed ? FireState.Shooting : FireState.None;
            soldier.Position = new Vector2(posX, posY);

            mouseOldState = mouseState;
            keyboardOldState = keyboardState;

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Gray);
            
            soldier.Draw(batch);
            foreach (BulletEntity bullet in bullets)
            {
                bullet.Draw(batch);
            }
            
            base.Draw(gameTime);
        }
    }
}
